#include "constant.h"
#include "namespace.h"
#include "errorlog.h"
#include "type.h"

Constant::Constant( Kind kind, const YYLTYPE& loc )
    : Operator( Operator::Constant, loc ), m_kind( kind ), m_resolvedNode(0)
{
}

Constant::Constant( int number, const QString& metric, const YYLTYPE& loc )
    : Operator( Operator::Constant, loc ), m_int( number ), m_string( metric ), m_kind( Int ), m_resolvedNode(0)
{
}

Constant::Constant( double number, const QString& metric, const YYLTYPE& loc )
    : Operator( Operator::Constant, loc ), m_float( number ), m_string( metric ), m_kind( Float ), m_resolvedNode(0)
{
}

Constant::Constant( const QString& str, Kind type, const YYLTYPE& loc )
    : Operator( Operator::Constant, loc ), m_string( str ), m_kind( type ), m_resolvedNode(0)
{
}

int Constant::intValue() const
{
    Q_ASSERT( m_kind == Int );
	return m_int;
}

int Constant::floatValue() const
{
    Q_ASSERT( m_kind == Float );
	return m_float;
}

QString Constant::metric() const
{
    Q_ASSERT( m_kind == Int || m_kind == Float );
	return m_string;
}

QString Constant::stringValue() const
{
    Q_ASSERT( m_kind == String || m_kind == TagIdentifier || m_kind == VariableIdentifier || m_kind == TagListIdentifier );
	return m_string;
}

void Constant::resolveAll()
{
    if ( this->m_kind == VariableIdentifier )
    {
        m_resolvedNode = nspace()->resolve(m_string);
        if ( !m_resolvedNode )
            ErrorLog::error( location(), "Could not resolve identifier " + m_string );
    }
    else if ( m_kind == LambdaParameter )
    {
        Node* prev = this;
        Node* p = parentNode();
        while( p )
        {
            if ( dynamic_cast<BinaryOperator*>(p) )
            {
                BinaryOperator* op = dynamic_cast<BinaryOperator*>(p);
                if ( op->operatorKind() == Operator::Filter && op->secondArgument() == prev )
                    break;
            }
            prev = p;
            p = p->parentNode();
        }
        if ( !p )
            ErrorLog::error(location(), "The lambda parameter $$ can only be used in a lambda expression");
    }

    this->Node::resolveAll();
}

Type Constant::type()
{
    switch( m_kind )
    {
    case Int:
    case Float:
    case String:
    case True:
    case False:
    case Null:
    case TagIdentifier:
        return Type(Type::Scalar);
    case TagListIdentifier:
        return Type(Type::List);
    case VariableIdentifier:
        if ( m_resolvedNode )
            return m_resolvedNode->type();
        return Type();
    case LambdaParameter:
        return Type(Type::Scalar);
    }
    return Type();
}
